Skip to content

#20897 - Expose selection custom field labels in the REST API#22475

Open
arthanson wants to merge 2 commits into
featurefrom
20897-cf
Open

#20897 - Expose selection custom field labels in the REST API#22475
arthanson wants to merge 2 commits into
featurefrom
20897-cf

Conversation

@arthanson

Copy link
Copy Markdown
Collaborator

Closes: #20897

Selection and multiple-selection custom fields are now rendered in the REST API as objects exposing both the stored value and its resolved label ({"value": …, "label": …}), matching the convention already used by built-in choice fields. The change affects read access only; values are still written by passing the raw value.

@arthanson

Copy link
Copy Markdown
Collaborator Author

@claude /review Only report on issues of potential concern; we don't need commentary on anything that doesn't require further consideration.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @arthanson's task in 2m 27s —— View job


PR Review: #20897 - Expose selection custom field labels in the REST API

  • Read changed files
  • Analyze diff
  • Post review findings

Two issues of potential concern:


1. Breaking API change for existing integrations

SELECT and MULTISELECT custom fields previously returned raw strings (e.g. "bar" / ["bar", "baz"]). After this PR they return {"value": "bar", "label": "Bar"} / [{"value": "bar", "label": "Bar"}, ...]. Any existing API client reading these fields will break silently — it receives a dict/list-of-dicts where it expected a string, with no error and no version signal.

This is the same tradeoff made when similar changes were applied to built-in choice fields, so it may be intentional, but it should be called out explicitly in the PR as a breaking change.


2. Potential 500 error when a client writes back the read format for MULTISELECT

to_internal_value in customfields.py passes SELECT/MULTISELECT values through unchanged, so if a client naively echoes back what it read (a list of dicts) for a MULTISELECT write, that list reaches CustomField.validate() unmodified. The validation at customfields.py:825-826 then does:

if not set(value).issubset(self.choice_set.values):

set() cannot hash dicts, so this raises an unhandled TypeError rather than a ValidationError, which surfaces as a 500. Fix this →

The SELECT case produces a legible ValidationError("Invalid choice...") rather than a 500, so it is less urgent, but the MULTISELECT case should be addressed.

A minimal fix in to_internal_value would be to extract the raw value when a dict or list-of-dicts is submitted for these types, matching how OBJECT/MULTIOBJECT values are normalized on write.


@arthanson

Copy link
Copy Markdown
Collaborator Author

pre classed review 1 is known it's a breaking change. 2 fixed - now consistent across the board: read returns {value, label}; write takes the raw value; the object form is rejected with a 400 for both selection types - same as built-in choice fields.

@arthanson arthanson marked this pull request as ready for review June 16, 2026 23:27
@arthanson arthanson requested review from a team and jnovinger and removed request for a team June 16, 2026 23:27
```json
"custom_fields": {
"cluster": {
"value": 1,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads like a PK integer, but I think this would actually be a database stored string, yeah?

}
```

As with built-in choice fields, selection custom fields are written by passing the raw value (e.g. `"cluster": 1`), not the `{value, label}` object returned on read.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with built-in choice fields, selection custom fields are written by passing the raw value (e.g. "site_type": "datacenter"), not the {value, label} object returned on read, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants